﻿using DataConvertLib;
using System;
using System.IO;
using YWM.Common;

namespace YWM.Communication
{
    /// <summary>
    /// ModBusRTU工具
    /// </summary>
    public static class ModBusRTUUtil
    {
        #region 获取请求消息

        #region 读取
        /// <summary>
        /// 获取读输出线圈请求消息
        /// </summary>
        /// <param name="slaveId">从设备Id</param>
        /// <param name="addr">线圈起始地址</param>
        /// <param name="count">读取数量</param>
        public static (byte[], int) Get0x01RequestMessage(byte slaveId, short addr, short count)
        {
            return GetReadRequestMessage(slaveId, ModBusFunction.Read01, addr, count);
        }

        /// <summary>
        /// 获取读输入线圈请求消息
        /// </summary>
        /// <param name="slaveId">从设备Id</param>
        /// <param name="addr">线圈起始地址</param>
        /// <param name="count">读取数量</param>
        public static (byte[], int) Get0x02RequestMessage(byte slaveId, short addr, short count)
        {
            return GetReadRequestMessage(slaveId, ModBusFunction.Read02, addr, count);
        }

        /// <summary>
        /// 获取读输出寄存器请求消息
        /// </summary>
        /// <param name="slaveId">从设备Id</param>
        /// <param name="addr">寄存器起始地址</param>
        /// <param name="count">读取数量</param>
        public static (byte[], int) Get0x03RequestMessage(byte slaveId, short addr, short count)
        {
            return GetReadRequestMessage(slaveId, ModBusFunction.Read03, addr, count);
        }

        /// <summary>
        /// 获取读输入寄存器请求消息
        /// </summary>
        /// <param name="slaveId">从设备Id</param>
        /// <param name="addr">寄存器起始地址</param>
        /// <param name="count">读取数量</param>
        public static (byte[], int) Get0x04RequestMessage(byte slaveId, short addr, short count)
        {
            return GetReadRequestMessage(slaveId, ModBusFunction.Read04, addr, count);
        }

        /// <summary>
        /// 获取读请求消息
        /// </summary>
        /// <param name="slaveId">从设备Id</param>
        /// <param name="modBusFunction">功能码</param>
        /// <param name="addr">起始地址</param>
        /// <param name="count">读取数量</param>
        /// <returns></returns>
        public static (byte[], int) GetReadRequestMessage(byte slaveId, ModBusFunction modBusFunction, short addr, short count)
        {
            byte[] middles = { (byte)(count >> 8), (byte)count };
            byte function = (byte)modBusFunction;
            int responseCount = 5;
            if (function > 2)
            {
                responseCount += count * 2;
            }
            else
            {
                responseCount += count / 8 + (count % 8 == 0 ? 0 : 1);
            }
            return (GetFinallyRequestMessage(slaveId, modBusFunction, addr, middles), responseCount);
        }
        #endregion

        #region 写入
        /// <summary>
        /// 获取写单个线圈请求消息
        /// </summary>
        /// <param name="slaveId">从设备Id</param>
        /// <param name="addr">线圈地址</param>
        /// <param name="isSet">是否置位线圈</param>
        /// <returns></returns>
        public static (byte[], int) Get0x05RequestMessage(byte slaveId, short addr, bool isSet)
        {
            byte[] middles = { (byte)(isSet ? 0xFF : 0x00), 0x00 };
            byte[] data = GetFinallyRequestMessage(slaveId, ModBusFunction.Wirte05, addr, middles);
            return (data, data.Length);
        }

        /// <summary>
        /// 获取写单个寄存器请求消息
        /// </summary>
        /// <param name="slaveId">从设备Id</param>
        /// <param name="addr">寄存器地址</param>
        /// <param name="value">单个寄存器的值</param>
        /// <returns></returns>
        public static (byte[], int) Get0x06RequestMessage(byte slaveId, short addr, short value)
        {
            byte[] middles = ByteArrayLib.GetByteArrayFromShort(value);
            return Get0x06RequestMessage(slaveId, addr, middles);
        }

        /// <summary>
        /// 获取写单个寄存器请求消息
        /// </summary>
        /// <param name="slaveId">从设备Id</param>
        /// <param name="addr">寄存器地址</param>
        /// <param name="values">单个寄存器的值</param>
        /// <returns></returns>
        public static (byte[], int) Get0x06RequestMessage(byte slaveId, short addr, byte[] values)
        {
            byte[] data = GetFinallyRequestMessage(slaveId, ModBusFunction.Wirte06, addr, values);
            return (data, data.Length);
        }

        /// <summary>
        /// 获取写多个线圈请求消息
        /// </summary>
        /// <param name="slaveId">从设备Id</param>
        /// <param name="addr">线圈起始地址</param>
        /// <param name="isSets">多个线圈的数据(bool数组)</param>
        /// <returns></returns>
        public static (byte[], int) Get0x0FRequestMessage(byte slaveId, short addr, bool[] isSets)
        {
            byte[] values = ByteArrayLib.GetByteArrayFromBoolArray(isSets);
            int valuesCount = values.Length;
            byte[] middles = new byte[3 + valuesCount];
            byte[] nums = ByteArrayLib.GetByteArrayFromShort((short)isSets.Length);
            middles[0] = nums[0];
            middles[1] = nums[1];
            middles[2] = (byte)valuesCount;
            Array.Copy(values, 0, middles, 3, valuesCount);
            return (GetFinallyRequestMessage(slaveId, ModBusFunction.Wirte0F, addr, middles), 8);
        }

        /// <summary>
        /// 获取写多个寄存器请求消息
        /// </summary>
        /// <param name="slaveId">从设备Id</param>
        /// <param name="addr">寄存器起始地址</param>
        /// <param name="values">多个寄存器的数据(字节数组)</param>
        /// <returns></returns>
        public static (byte[], int) Get0x10RequestMessage(byte slaveId, short addr, byte[] values)
        {
            int len = values.Length;
            byte[] nums = ByteArrayLib.GetByteArrayFromShort((short)(len >> 1));
            byte[] middles = new byte[3 + len];
            middles[0] = nums[0];
            middles[1] = nums[1];
            middles[2] = (byte)len;
            Array.Copy(values, 0, middles, 3, len);
            return (GetFinallyRequestMessage(slaveId, ModBusFunction.Wirte10, addr, middles), 8);
        }
        #endregion

        private static byte[] GetFinallyRequestMessage(byte slaveId, ModBusFunction modBusFunction, short addr, byte[] middles)
        {
            byte[] buffer = new byte[6 + middles.Length];
            buffer[0] = slaveId;
            buffer[1] = (byte)modBusFunction;
            buffer[2] = (byte)(addr >> 8);
            buffer[3] = (byte)addr;
            Array.Copy(middles, 0, buffer, 4, middles.Length);
            byte[] crc = CRCUtil.CRC16(buffer, buffer.Length - 2);
            buffer[buffer.Length - 2] = crc[0];
            buffer[buffer.Length - 1] = crc[1];
            return buffer;
        }
        #endregion

        #region 获取响应消息
        /// <summary>
        /// 分析响应消息
        /// </summary>
        /// <param name="requestBuffer">请求消息</param>
        /// <param name="responseBuffer">响应消息</param>
        /// <param name="count">通过计算得出的响应消息数量</param>
        /// <returns></returns>
        /// <exception cref="InvalidDataException"></exception>
        /// <exception cref="InvalidOperationException"></exception>
        public static ModBusAnalysis AnalysisResponseMessage(byte[] requestBuffer, byte[] responseBuffer, int count)
        {
            if (count < 6 || requestBuffer == null || responseBuffer == null || responseBuffer.Length != count)
            {
                return null;
            }

            if (!CRCUtil.CheckCRC16(responseBuffer))
            {
                return null;
            }

            byte slaveId = responseBuffer[0];
            byte function = responseBuffer[1];
            if (slaveId != requestBuffer[0] || function != requestBuffer[1])
            {
                return null;
            }

            byte[] data = null;
            if (function < 0x05)
            {
                int length = responseBuffer[2];
                if (length == responseBuffer.Length - 5)
                {
                    data = new byte[length];
                    Array.Copy(responseBuffer, 3, data, 0, data.Length);
                }
            }
            else
            {
                //暂不做写入响应分析,只截出除从设备ID，功能码，校验码之外的数据
                bool equal = true;
                for (int i = 2; i < 6; i++)
                {
                    if (responseBuffer[i] != requestBuffer[i])
                    {
                        equal = false;
                        break;
                    }
                }
                if (equal)
                {
                    data = new byte[responseBuffer.Length - 4];
                    Array.Copy(responseBuffer, 2, data, 0, data.Length);
                }
            }

            if(data == null)
            {
                return null;
            }

            ModBusAnalysis modBusAnalysis = new ModBusAnalysis
            {
                Identifier = slaveId,
                ModBusFunction = (ModBusFunction)function,
                Data = data
            };
            return modBusAnalysis;
        }
        #endregion
    }
}
